#include "mainwindow.h"
#include "ui_mainwindow.h"

void MainWindow::swapBlock(int i0, int j0, int i1, int j1)
{
    qDebug() << "[slot] swap i1=" << i0 << ", j1=" << j0 << ", i2=" << i1 << ", j2=" << j1;

    int tmp = mat[i0][j0];
    mat[i0][j0] = mat[i1][j1];
    mat[i1][j1] = tmp;

    // 能否进行消除，不能则是无效操作，恢复之前的状态
    match_scan();
    bool eliminatable = false;
    for (int i = 0; i < COL_SIZE; i++) {
        for (int j = 0; j < ROW_SIZE; j++) {
            if (eliminated[i][j])
                eliminatable = true;
        }
    }
    if (!eliminatable) {
        // 状态恢复
        int tmp = mat[i0][j0];
        mat[i0][j0] = mat[i1][j1];
        mat[i1][j1] = tmp;
        return;
    }

    DragPushButton* p0 = btns[i0][j0];
    QPalette pal0 = p0->palette();
    pal0.setColor(QPalette::Button, b_color[mat[i0][j0]]);
    p0->setPalette(pal0);

    DragPushButton* p1 = btns[i1][j1];
    QPalette pal1 = p1->palette();
    pal1.setColor(QPalette::Button, b_color[mat[i1][j1]]);
    p1->setPalette(pal1);

    flush(true);
}

int MainWindow::cal_score()
{
    // 统计待消除方块数量
    int tmp = 0;
    for (int i = 0; i < COL_SIZE; i++) {
        for (int j = 0; j < ROW_SIZE; j++) {
            if (eliminated[i][j]) {
                tmp++;
            }
        }
    }

    // 刷新分数显示
    score += tmp * SCORE_RATIO;
    ui->lcdNumber->display(score);

    return tmp;
}

void MainWindow::match_scan()
{
    // 扫描行
    for (int i = 0; i < COL_SIZE; i++) {
        int seq = 0;
        int preColor = -1;
        for (int j = 0; j <= ROW_SIZE; j++) {
            if (j == ROW_SIZE) {
                // 尾部结算
                if (seq >= 3) {
                    while (seq--) {
                        eliminated[i][j - seq - 1] = true;
                    }
                }
            } else {
                // 内部结算
                if (mat[i][j] != preColor) {
                    // 断了，结算一下，至少连续3个
                    if (seq >= 3) {
                        while (seq--) {
                            // 标记需要被消除
                            eliminated[i][j - seq - 1] = true;
                        }
                    }
                    preColor = mat[i][j];
                    seq = 1;
                } else {
                    seq++;
                }
            }
        }
    }

    // 扫描列
    for (int j = 0; j < ROW_SIZE; j++) {
        int seq = 0;
        int preColor = -1;
        for (int i = 0; i <= COL_SIZE; i++) {
            if (i == COL_SIZE) {
                // 尾部结算
                if (seq >= 3) {
                    while (seq--) {
                        eliminated[i - seq - 1][j] = true;
                    }
                }
            } else {
                // 内部结算
                if (mat[i][j] != preColor) {
                    // 断了，结算一下，至少连续3个
                    if (seq >= 3) {
                        while (seq--) {
                            // 标记需要被消除
                            eliminated[i - seq - 1][j] = true;
                        }
                    }
                    preColor = mat[i][j];
                    seq = 1;
                } else {
                    seq++;
                }
            }
        }
    }
}

void MainWindow::drop(bool animate)
{
    /*
     * 动画流程：统计补充方块量，然后直接把颜色修改到最终状态
     *  1. 把颜色修改到最终状态
     *  2. 从下到上在每个未被消除的方块位置放置方块，多出来的放在超过第一行的位置，连续
     *  3. 线性动画移动到目标位置，定时器刷新屏幕
     */

    int dropBlockNum[ROW_SIZE]; // 每一列需要补充的方块数量
    for (int j = 0; j < ROW_SIZE; j++) {
        dropBlockNum[j] = 0;
        for (int i = 0; i < COL_SIZE; i++) {
            if (eliminated[i][j]) {
                dropBlockNum[j]++;
            }
        }
        // 线性表删除操作，双指针算法，r是读取针，w是写入针
        int r = COL_SIZE - 1, w = COL_SIZE - 1;
        while (r >= 0) {
            while (r >= 0 && eliminated[r][j])
                r--;
            if (r >= 0 && w >= 0)
                mat[w][j] = mat[r][j];
            r--;
            w--;
        }
        // 修正颜色，新方块色彩随机
        for (int i = 0; i < dropBlockNum[j]; i++) {
            mat[i][j] = qrand() % BLOCK_TYPE_NUM; // 补充随机色彩
        }
        // 把方块刷到目标颜色
        for (int i = 0; i < COL_SIZE; i++) {
            DragPushButton* p = btns[i][j];
            QPalette palette = p->palette();
            palette.setColor(QPalette::Button, b_color[mat[i][j]]);
            p->setPalette(palette);
        }
    }

    //后续是动画效果，如果关闭了动画则跳过
    if (animate) {
        // 置于最底层，保证超过顶栏时可以被遮挡，且防止被鼠标操作
        for (int i = 0; i < COL_SIZE; i++) {
            for (int j = 0; j < ROW_SIZE; j++) {
                btns[i][j]->lower();
            }
        }

        // 计算动画起始位置
        QPoint p_start[COL_SIZE][ROW_SIZE];
        for (int j = 0; j < ROW_SIZE; j++) {
            // 移动方块到目标位置，双指针算法，r是读取针，w是写入针
            int r = COL_SIZE - 1, w = COL_SIZE - 1;
            while (w >= 0) {
                while (r >= 0 && eliminated[r][j])
                    r--;
                if (r < 0) // 负坐标位置，一次取点上移一格
                    r--;
                p_start[w][j] = QPoint(
                    GAME_FIELD_X + BLOCK_EDGE * j + BLOCK_MARGIN,
                    GAME_FIELD_Y + BLOCK_EDGE * r + BLOCK_MARGIN);
                r--;
                w--;
            }
        }

        // 计算终点位置，也就是按钮原始位置
        QPoint p_end[COL_SIZE][ROW_SIZE];
        for (int i = 0; i < COL_SIZE; i++) {
            for (int j = 0; j < ROW_SIZE; j++) {
                p_end[i][j] = QPoint(
                    GAME_FIELD_X + BLOCK_EDGE * j + BLOCK_MARGIN,
                    GAME_FIELD_Y + BLOCK_EDGE * i + BLOCK_MARGIN);
            }
        }

        // 计算动画启动延时并组装串行动画组
        QSequentialAnimationGroup*** pSeqGroups = new QSequentialAnimationGroup**[COL_SIZE];
        for (int i = 0; i < COL_SIZE; i++) {
            pSeqGroups[i] = new QSequentialAnimationGroup*[ROW_SIZE];
            for (int j = 0; j < ROW_SIZE; j++) {
                QSequentialAnimationGroup* p = new QSequentialAnimationGroup(this);
                // 计算时间延迟
                int dt = (p_end[i][j].y() - p_start[i][j].y()) / ANIME_SPEED;
                p->addPause(dt);
                // 计算位移动画
                QPropertyAnimation* pPosAnime = new QPropertyAnimation(btns[i][j], "pos");
                // pPosAnime->setDuration(1000);
                pPosAnime->setStartValue(p_start[i][j]);
                pPosAnime->setEndValue(p_end[i][j]);
                // pPosAnime->setEasingCurve(QEasingCurve::InOutQuad);
                p->addAnimation(pPosAnime);
                pSeqGroups[i][j] = p;
            }
        }

        // 组装并行动画组并启动
        QParallelAnimationGroup* pPalGroup = new QParallelAnimationGroup(this);
        for (int i = 0; i < COL_SIZE; i++) {
            for (int j = 0; j < ROW_SIZE; j++) {
                pPalGroup->addAnimation(pSeqGroups[i][j]);
            }
        }
        pPalGroup->start();

        // 重新置于顶层，响应鼠标
        for (int i = 0; i < COL_SIZE; i++) {
            for (int j = 0; j < ROW_SIZE; j++) {
                btns[i][j]->raise();
            }
        }
    }

    // 都刷到不消除状态
    for (int i = 0; i < COL_SIZE; i++) {
        for (int j = 0; j < ROW_SIZE; j++) {
            eliminated[i][j] = false;
        }
    }
}
